package org.infoset.xml;

import java.net.*;
import java.lang.reflect.*;

/**
 * This class represents an XML name.  It may be qualified or unqualified.
 * @author <a href="mailto:alex@milowski.com">R. Alexander Milowski</a>
 */
public class Name implements Comparable {

   private static boolean debug = false;
//   private static Method getNameTable;
   static {
      String dflag = System.getProperty("org.infoset.xml.Name.debug");
      if (dflag!=null) {
         debug = dflag.equals("true");
      }
//      String implClassName = System.getProperty("org.infoset.xml.impl");
//      if (implClassName==null) {
//         implClassName = "com.smallx.xml.infoset.memory.MemoryNameTable";
//      }
//      try {
//         Class implClass = Class.forName(implClassName);
//         getNameTable = implClass.getDeclaredMethod("getDefaultNameTable");
//      } catch (Exception ex) {
//         ex.printStackTrace();
//      }
   }
   public static final String NO_PREFIX = "";
   /**
    * The XML namespace as specified in XML 1.0.
    */
   public static final String XML_NAMESPACE = "http://www.w3.org/XML/1998/namespace";

   /**
    * The XML namespace as a URI instance.
    */

   public static final URI XML_NAMESPACE_URI = URI.create(XML_NAMESPACE);
   public static final URI NO_NAMESPACE = URI.create("NO:NAMESPACE");
   
   URI namespaceName;
   String localName;

   /**
    * Constructs a name with the given namespace and local name.
    * @param namespaceName The namespace name to use.
    * @param localName The local name to use.
    */
   public Name(URI namespaceName,String localName) {
      this.namespaceName = namespaceName;
      if (namespaceName==null) {
         throw new IllegalArgumentException("A namespace name must be specified for this constructor.");
      }
      if (debug && namespaceName.equals(NO_NAMESPACE) && namespaceName!=NO_NAMESPACE) {
         throw new IllegalArgumentException("NO:NAMESPACE is reserved for internal use.");
      }
      this.localName = localName;
      /* These should be checked on serialization.
      if (localName.length()<1) {
         throw new IllegalArgumentException("A local name must have one or more name characters.");
      }
      if (localName.indexOf(':')>=0) {
         throw new IllegalArgumentException("A colon is not allowed in a local name: "+localName);
      }*/
   }

   /**
    * Constructs an unqualifed name with the given local name.
    * @param localName The local name to use.
    */
   public Name(String localName) {
      this(NO_NAMESPACE,localName);
   }

   /**
    * Returns the local name of the name.
    */
   public String getLocalName() {
      return localName;
   }

   /**
    * Returns the namespace name of the name.
    */
   public URI getNamespaceName() {
      return namespaceName;
   }

   /**
    * Returns a string representation of the name where curly braces are used to delimit the namespace name (e.g. {http://www.w3.org/XML/1998/namespace}lang for xml:lang).
    */
   public String toString() { 
      return namespaceName==NO_NAMESPACE ? localName : '{'+namespaceName.toString()+'}'+localName;
   }

   public boolean equals(Object other) {
      if (this==other) {
         return true;
      }
      if (other instanceof Name) {
         Name toCompare = (Name)other;
         if (toCompare.getNamespaceName()==NO_NAMESPACE) {
            return namespaceName!=NO_NAMESPACE ? false : toCompare.getLocalName().equals(localName);
         } else if (namespaceName!=NO_NAMESPACE) {
            return namespaceName.equals(toCompare.getNamespaceName()) ? localName.equals(toCompare.getLocalName()) : false;
         } else {
            return false;
         }
      } else {
         return false;
      }
   }

   public int compareTo(Object obj) {
      if (obj instanceof Name) {
         if (obj==this) {
            return 0;
         }
         Name other = (Name)obj;
         int nsCompare = namespaceName.compareTo(other.getNamespaceName());
         if (nsCompare==0) {
            return localName.compareTo(other.getLocalName());
         } else {
            return nsCompare;
         }
      } else {
         return 1;
      }
   }
   
//   public static NameTable getDefaultNameTable() {
//      try {
//         return (NameTable)getNameTable.invoke(null);
//      } catch (Exception ex) {
//         throw new RuntimeException(ex.getMessage());
//      }
//   }
//   
   public static String getPrefix(String qname) 
      throws XMLException
   {
      int colon = qname.indexOf(':');
      if (colon<0) {
         return NO_PREFIX;
      }
      if (colon==0) {
         throw new XMLException("Names may not start with a colon: "+qname);
      }
      return qname.substring(0,colon);
   }
   
   public int hashCode() {
      //return localName.hashCode();
      //return namespaceName==NO_NAMESPACE ? localName.hashCode() : localName.hashCode()+namespaceName.hashCode();
      int hash = 1;
      hash = hash * 31 + localName.hashCode();
      hash = hash * 31 + (namespaceName == NO_NAMESPACE ? 0 : namespaceName.hashCode());
      return hash;
   }
   
   public static Name create(URI namespace,String localname) {
      return new Name(namespace, localname);
   }
   
   public static Name create(String value) {
      if (value.charAt(0)=='{') {
         String nsPrep = value.substring(1);
         int end = nsPrep.indexOf('}');
         if (end<0) {
            throw new IllegalArgumentException("Name syntax does not contain an end curly brace: "+value);
         }
         String ns = nsPrep.substring(0,end);
         String local = nsPrep.substring(end+1);
         if (local.length()==0) {
            throw new IllegalArgumentException("The local name is empty: "+value);
         }
         return new Name(URI.create(ns), local);
      } else {
         return new Name(value);
      }
   }
   
}
